
/*  Created by verycloud on 2020/09/18.
    Copyright © 2019 verycloud. All rights reserved.

    build in cmdline
    gcc -g -O0 main.c -lssl -lcrypto

    this is used in spicy client online
*/
#include <openssl/evp.h>
#include <openssl/md5.h>
#include <string.h>
#include <stdio.h>
/*
 *  @parme in  str end by '\0'
 *  @parme out buf size >= 33
 */
void make_md5(const char* in,char* out)
{
    int inSize = (int)strlen(in);

    unsigned char buf[16];
    MD5((const unsigned char*)in, inSize, (unsigned char*)buf);

    char tmp[3] = {0};
    for (int i = 0; i < 16; i++) {
        sprintf(tmp, "%02x", (int)buf[i]);
        strcat(out,tmp);
    }
}

void make_base64_2K(char s[])
{
    unsigned char buf[2048];
    int len = EVP_EncodeBlock((unsigned char*)buf, (const unsigned char*)s, strlen(s));
    strncpy(s, buf, len);
}

void m_append(char s[], char tmp[], int len)
{
    if(s[0] == 0)
    {
        strncpy(s, tmp, len);
    }else
    {
        strncat(s, tmp, len);
    }
}

int string_find(char tmp[], char c)
{
    int i;
    for(i=0; i<strlen(tmp); i++)
    {
        if(tmp[i] == c)
        {
            break;
        }
    }
    return i;
}

char* vcd_encode_crypto(const char* in, char* out)
{
    int i, j, k = 0;

    char str[1024] = {0};
    strncpy(str, in, 1024);

    const char* public_key = "!Knowledge@#is#@power~@";
    char* lockstream = "st=lDEFABCNOPyzghi_jQRST-UwxkVWXYZabcdefIJK67nopqr89LMmGH012345uv";

    int strLen  = (int)strlen(str);
    int lockLen = (int)strlen(lockstream);

    char randomLock = str[strLen-1];
    int lockCount = (int)(strchr(lockstream, randomLock) - lockstream);

    // public_key + randomLock + '\0'
    int keyLen = (int)strlen(public_key) + 1 + 1;
    char* keyBuf = malloc(keyLen);
    sprintf(keyBuf, "%s%c", public_key, randomLock);

    char password[33] = {0};
    make_md5(keyBuf, password);

    make_base64_2K(str);

    char tmpStream[200] = {0};
    for(i=0,j=0,k=0; i<strlen(str); i++)
    {
        k = (k == strlen(password)) ? 0 : k;
        j = (lockCount + (int)password[k] + string_find(lockstream, str[i])) % lockLen;
        tmpStream[strlen(tmpStream)] = lockstream[j];
        k++;
    }
    tmpStream[strlen(tmpStream)] = randomLock;

    strcpy(out, tmpStream);
    free(keyBuf);
    return out;
}

char* vcd_decode_crypto(const char* str, char* out){

    if (!str || !out) return NULL;

    const char* public_key = "!Knowledge@#is#@power~@";
    const char* lockstream = "st=lDEFABCNOPyzghi_jQRST-UwxkVWXYZabcdefIJK67nopqr89LMmGH012345uv";

    int strLen  = (int)strlen(str);
    int lockLen = (int)strlen(lockstream);

    char randomLock = str[strLen-1];
    int lockCount = (int)(strchr(lockstream, randomLock) - lockstream);

    // public_key + randomLock + '\0'
    int keyLen = (int)strlen(public_key) + 1 + 1;
    char* keyBuf = malloc(keyLen);
    sprintf(keyBuf, "%s%c", public_key, randomLock);

    char password[33] = {0};
    make_md5(keyBuf, password);
    //printf("make_md5: %s\n", password);

    int k = 0;
    char* beforeBase64 = malloc(strLen-1);
    for(int i = 0; i < strLen-1;i++) {
        k = k == strlen(password) ? 0 : k;

        int j = (int)(strchr(lockstream,str[i])-lockstream) - lockCount - (int)password[k];
        while(j < 0)
            j = j + lockLen;
        beforeBase64[i] = lockstream[j];
        k++;
    }

    EVP_DecodeBlock((unsigned char *)out, (unsigned char *)beforeBase64, strLen-1);

    free(keyBuf);
    free(beforeBase64);
    return out;
}

int main(int argc, char** argv)
{
    if (argc < 2) {
        printf("usage: ./a.out test_string\n");
        return 0;
    }
    printf("test_string:%s\n", argv[1]);
    char encode_out[1024] = {0};
    vcd_encode_crypto(argv[1], encode_out);
    printf("encode out: %s\n", encode_out);
    char decode_out[1024] = {0};
    vcd_decode_crypto(encode_out, decode_out);
    printf("decode out: %s\n", decode_out);
    return 0;
}
